package com.xyy.saas.payment.payment.core.service;

import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.cores.vo.FuminRecordBo;
import com.xyy.saas.payment.cores.vo.QueryFuminRecordBo;
import com.xyy.saas.payment.cores.vo.QueryAccountBalanceDO;
import com.xyy.saas.payment.cores.vo.QueryFuminRecordVo;
import com.xyy.saas.payment.adpater.fumin.spring.YBMSpringConfiguration;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.enums.FBankTradeTypeEnum;
import com.xyy.saas.payment.common.enums.FrozenStatusEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.FrozenBo;
import com.xyy.saas.payment.cores.enums.BusinessIdTypeEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.dao.mapper.FrozenSettlementMapper;
import com.xyy.saas.payment.dao.model.FrozenSettlement;
import com.xyy.saas.payment.dao.model.PayAccount;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.merchant.core.dto.FrozenDto;
import com.xyy.saas.payment.payment.core.dto.VirtualAccountDto;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.settle.core.service.AccountService;
import com.xyy.saas.payment.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;

/**
 * 结算资金冻结服务
 */
@Slf4j
@Service
public class FrozenSettlementService {
    @Resource
    private FrozenSettlementMapper frozenSettlementMapper;
    @Resource
    private YBMSpringConfiguration ybmSpringConfiguration;
    @Resource
    private PayAccountService payAccountService;
    @Resource
    private RouteManager routeManager;
    @Resource
    private LeafUtil leafUtil;
    @Autowired
    private AccountService accountService;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    /**
     * 资金冻结
     */
    public void frozenBalance(VirtualAccountDto virtualAccountDto) throws PaymentException {
        FrozenSettlement frozenSettlement = null;
        PaymentException exception = null;

        try {
            log.info("FrozenSettlementService#frozenBalance，start, signResult={}", JSONObject.toJSONString(virtualAccountDto));

            // 只处理结算动账通知
            if(!FBankTradeTypeEnum.SETTLE.getType().equals(virtualAccountDto.getTrade_type())) {
                log.warn("FrozenSettlementService#frozenBalance, ignore trade type, signResult={}", JSONObject.toJSONString(virtualAccountDto));
                return;
            }

            // 2.查询待冻结账单
            PayAccount payAccount = payAccountService.getPayAccountByAccountId(virtualAccountDto.getCust_merchant_no());

            if(payAccount == null) {
                throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
            }

            if(!BusinessIdTypeEnum.EC_POP.getType().equals(payAccount.getBusinessIdType())) {
                log.warn("FrozenSettlementService#frozenBalance, ignore business order type, signResult={}", JSONObject.toJSONString(virtualAccountDto));
                return;
            }

            Date tradeDate = ConcurrentSimpleDateFormatUtil.parse(virtualAccountDto.getTrade_date(), DateUtil.DATEFORMATDAY);
            FrozenSettlement condition = new FrozenSettlement();
            condition.setMerchantNo(virtualAccountDto.getMerchant_no());
            condition.setAccountId(virtualAccountDto.getCust_merchant_no());
            condition.setAccountNo(virtualAccountDto.getAccount_no());
            condition.setBusinessOrderType(payAccount.getBusinessIdType());
            condition.setTradeType(virtualAccountDto.getTrade_type());
            condition.setFrozenDate(tradeDate);
            condition.setStatus(FrozenStatusEnum.FROZEN_WAIT.getStatus());
            List<FrozenSettlement> items = frozenSettlementMapper.queryFrozenSettlement(condition);
            log.info("FrozenSettlementService#frozenBalance, condition1={}, items={}",
                    JSONObject.toJSONString(condition), JSONObject.toJSONString(items));
            boolean needFrozen = false;

            /**
             * 查询到待冻结账单情况：
             *  1.冻结账单一条数据，直接冻结
             *  2.冻结账单多条数据，比较价格
             *   2.1有相同的，直接冻结
             *   2.2无相同的，随便找一个冻结（这种CASE需要遇到后需要分析，或许是因为时间临界点问题导致计算的待冻结金额不准确）
             */
            if(!CollectionUtils.isEmpty(items)) {
                for(FrozenSettlement item : items) {
                    if(AmountCastUtil.equalsAmount(item.getAmount(), virtualAccountDto.getAmount())) {
                        frozenSettlement = item;
                        break;
                    }
                }

                frozenSettlement = frozenSettlement == null ? items.get(NumberUtils.INTEGER_ZERO) : frozenSettlement;
                needFrozen = true;
            }
            /**
             * 未查询到待冻结账单情况：
             *  1.待冻结账单已执行，动账消息重复或晚于定时任务
             *  2.待冻结账单未生成，生成冻结账单任务异常
             */
            else {
                condition.setStatus(null);
                items = frozenSettlementMapper.queryFrozenSettlement(condition);
                log.info("FrozenSettlementService#frozenBalance, condition2={}, items={}",
                        JSONObject.toJSONString(condition), JSONObject.toJSONString(items));

                if(!CollectionUtils.isEmpty(items)) {
                    loop:
                    for(FrozenSettlement item : items) {
                        switch(FrozenStatusEnum.get(item.getStatus())) {
                            case FROZEN_SUCCESS:
                            case SUB_UNFROZEN_SUCCESS:
                            case UNFROZEN_SUCCESS:
                                if(AmountCastUtil.equalsAmount(item.getFrozenAmount(), virtualAccountDto.getAmount())) {
                                    frozenSettlement = null;
                                    needFrozen = false;
                                    break loop;
                                }
                                else {
                                    frozenSettlement = FrozenSettlement.build(ybmSpringConfiguration.merchantNo(), payAccount.getAccountId(), virtualAccountDto.getAccount_no(),
                                            payAccount.getBusinessIdType(), FBankTradeTypeEnum.SETTLE.getType(), virtualAccountDto.getAmount(), tradeDate, tradeDate,
                                            FrozenStatusEnum.FROZEN_WAIT.getStatus(), FrozenStatusEnum.FROZEN_WAIT.getDesc());
                                    needFrozen = true;
                                    break;
                                }
                            case FROZEN_WAIT:
                            case FROZEN_FAIL:
                                frozenSettlement = item;
                                needFrozen = true;
                                break;
                        }
                    }
                }
                else {
                    frozenSettlement = FrozenSettlement.build(ybmSpringConfiguration.merchantNo(), payAccount.getAccountId(), virtualAccountDto.getAccount_no(),
                            payAccount.getBusinessIdType(), FBankTradeTypeEnum.SETTLE.getType(), virtualAccountDto.getAmount(), tradeDate, tradeDate,
                            FrozenStatusEnum.FROZEN_WAIT.getStatus(), FrozenStatusEnum.FROZEN_WAIT.getDesc());
                    needFrozen = true;
                }
            }

            if(needFrozen) {
                // 富民商户余额冻结
                FrozenBo frozenBo = routeManager.frozenBalance(FrozenDto.build(payAccount.getBusinessIdType(), payAccount.getAccountId(),
                        virtualAccountDto.getAmount(), leafUtil.getSnowflakeId()));
                // 冻结账单状态变更
                frozenSettlement.setFrozenAmount(virtualAccountDto.getAmount());
                frozenSettlement.setFrozenFlowNo(frozenBo.getFlowNo());
                frozenSettlement.setStatus(FrozenStatusEnum.FROZEN_SUCCESS.getStatus());
                frozenSettlement.setMessage(FrozenStatusEnum.FROZEN_SUCCESS.getDesc());
            }
        }
        catch(Exception ex) {
            log.error("FrozenBalance error, ciphertext=" + ex.getMessage(), ex);

            assert frozenSettlement != null;
            frozenSettlement.setStatus(FrozenStatusEnum.FROZEN_FAIL.getStatus());
            frozenSettlement.setMessage(FrozenStatusEnum.FROZEN_FAIL.getDesc());

            if(ex instanceof PaymentException) {
                exception = (PaymentException) ex;
                frozenSettlement.setMessage(FrozenStatusEnum.FROZEN_FAIL.getDesc() + "," + ((PaymentException) ex).getErrMsg());
            }
            else {
                exception = new PaymentException(ResultCodeEnum.FROZEN_SETTLEMENT_ERROR);
            }
        }

        int effects = 0;

        if(frozenSettlement != null) {
            effects  = frozenSettlement.getId() != null ? frozenSettlementMapper.updateByPrimaryKeySelective(frozenSettlement) :
                    frozenSettlementMapper.insert(frozenSettlement);
        }

        log.info("FrozenSettlementService#frozenBalance，end, signResult={}, effects={}", JSONObject.toJSONString(virtualAccountDto), effects);

        if(exception != null) {
            throw exception;
        }
    }

    public List<FrozenSettlement> getFrozenSettlement(String accountId, List<FrozenStatusEnum> statusList) {
        List<FrozenSettlement> result = new ArrayList<>();
        FrozenSettlement condition = new FrozenSettlement();

        // 兼容富民->平安换签，清空冻结余额场景
        if(accountId.contains(PayAccount.SUFFIX_DELETE)) {
            condition.setAccountId(accountId.substring(0, accountId.indexOf(Constants.UNDER_LINE)));
        }
        else {
            condition.setAccountId(accountId);
        }

        for(FrozenStatusEnum status : statusList) {
            condition.setStatus(status.getStatus());
            result.addAll(frozenSettlementMapper.queryFrozenSettlement(condition));
        }

        return result;
    }

    public void updateById(FrozenSettlement frozenSettlement) {
        frozenSettlementMapper.updateByPrimaryKeySelective(frozenSettlement);
    }

    /**
     * 提现时检查是否存在未冻结账单
     */
    public void checkWithdrawal(String businessIdType, String businessUserId) throws PaymentException {
        if(!BusinessIdTypeEnum.EC_POP.getType().equals(businessIdType)) {
            return;
        }

        String accountId =  payAccountService.getAccountIdByBusiness(businessIdType, businessUserId);

        if(StringUtils.isBlank(accountId)) {
            throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
        }

        // 非富民不检验
        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(accountId);

        if(!Constants.QIFUTONG.equals(thirdUserInfo.getThirdType())) {
            return;
        }

        // 1.查看富民结算记录
        List<FuminRecordBo> settlementRecords = getSettlementRecord(businessIdType, accountId);

        if(CollectionUtils.isEmpty(settlementRecords)) {
            return;
        }

        // 2.尝试冻结
        for(FuminRecordBo fuminRecordBo : settlementRecords) {
            try {
                VirtualAccountDto virtualAccountDto = VirtualAccountDto.build(fuminRecordBo, accountId);

                if(checkFrozenComplete(businessIdType, virtualAccountDto)) {
                    log.info("FrozenSettlementService#checkWithdrawal, already frozen, virtualAccountDto={}", JSONObject.toJSONString(virtualAccountDto));
                    continue;
                }

                frozenBalance(virtualAccountDto);
            }
            catch(Exception ex) {
                log.error(ex.getMessage(), ex);
                throw new PaymentException(ResultCodeEnum.FROZEN_SETTLEMENT_ERROR_WITHDRAWAL_IS_NOT_ALLOWED);
            }
        }
    }

    /**
     * 查询当天的结算记录
     * @param businessIdType
     * @return
     */
    private List<FuminRecordBo> getSettlementRecord(String businessIdType, String accountId) throws PaymentException {
        QueryFuminRecordVo queryFuminRecordVo = new QueryFuminRecordVo();
        queryFuminRecordVo.setBeginTime(DateUtil.getToday());
        queryFuminRecordVo.setEndTime(DateUtil.getToday());
        queryFuminRecordVo.setBusinessIdType(businessIdType);
        queryFuminRecordVo.setBusinessOrderType(businessIdType);

        List<FuminRecordBo> result = new ArrayList<>();
        int pageNo = 1;

        while(true) {
            queryFuminRecordVo.setPageNo(pageNo);
            QueryFuminRecordBo recordBos = getQueryFuminRecordBo(queryFuminRecordVo, accountId);

            for(FuminRecordBo recordBo : recordBos.getRecords()) {
                if(FBankTradeTypeEnum.ACCOUNT_HANDLE_TYPE.getType().equals(recordBo.getTradeType())) {
                    result.add(recordBo);
                }
            }

            if(recordBos.getRecords().size() < 20) {
                break;
            }

            pageNo++;
        }

        return result;
    }

    private QueryFuminRecordBo getQueryFuminRecordBo(QueryFuminRecordVo queryFuminRecordVo, String userId) throws PaymentException {
        QueryAccountBalanceDO queryAccountBalanceDO = new QueryAccountBalanceDO();
        queryAccountBalanceDO.setBeginTime(queryFuminRecordVo.getBeginTime());
        queryAccountBalanceDO.setEndTime(queryFuminRecordVo.getEndTime());
        queryAccountBalanceDO.setBusinessIdType(queryFuminRecordVo.getBusinessIdType());
        queryAccountBalanceDO.setBusinessUserId(queryFuminRecordVo.getBusinessId());
        queryAccountBalanceDO.setBusinessOrderType(queryFuminRecordVo.getBusinessOrderType());
        queryAccountBalanceDO.setPageNo(queryFuminRecordVo.getPageNo());
        queryAccountBalanceDO.setUserId(userId);
        String thirdUserId = accountService.getThirdUserIdByUserId(userId, Constants.QIFUTONG);
        queryAccountBalanceDO.setThirdUserId(thirdUserId);
        return routeManager.queryBalanceRecord(queryAccountBalanceDO);
    }

    /**
     * 冻结是否完成
     * @param virtualAccountDto
     * @return
     */
    private boolean checkFrozenComplete(String businessIdType, VirtualAccountDto virtualAccountDto) {
        try {
            Date tradeDate = ConcurrentSimpleDateFormatUtil.parse(virtualAccountDto.getTrade_date(), DateUtil.DATEFORMATDAY);
            FrozenSettlement condition = new FrozenSettlement();
            condition.setMerchantNo(virtualAccountDto.getMerchant_no());
            condition.setAccountId(virtualAccountDto.getCust_merchant_no());
            condition.setAccountNo(virtualAccountDto.getAccount_no());
            condition.setBusinessOrderType(businessIdType);
            condition.setTradeType(virtualAccountDto.getTrade_type());
            condition.setFrozenDate(tradeDate);
            condition.setStatus(FrozenStatusEnum.FROZEN_SUCCESS.getStatus());
            List<FrozenSettlement> items = frozenSettlementMapper.queryFrozenSettlement(condition);

            if(!CollectionUtils.isEmpty(items)) {
                for(FrozenSettlement item : items) {
                    if(AmountCastUtil.equalsAmount(item.getFrozenAmount(), virtualAccountDto.getAmount())) {
                        return true;
                    }
                }
            }
        }
        catch(Exception ex) {
            log.error(ex.getMessage(), ex);
        }

        return false;
    }
}